package SubjectString;

import java.util.Arrays;

public class RepeatedSubstringPattern {

/**
 * 难度：简单
 * 
 * 459. 重复的子字符串
 * 	给定一个非空的字符串，判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母，并且长度不超过10000。
 * 
 * 示例 1:
 * 	输入: "abab"
 * 	输出: True
 * 	解释: 可由子字符串 "ab" 重复两次构成。
 * 
 * 示例 2:
 * 	输入: "aba"
 * 	输出: False
 * 
 * 示例 3:
 * 	输入: "abcabcabcabc"
 * 	输出: True
 * 	解释: 可由子字符串 "abc" 重复四次构成。 (或者子字符串 "abcabc" 重复两次构成。)
 * 
 * */
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		RepeatedSubstringPattern rsp = new RepeatedSubstringPattern();
		System.out.println(rsp.repeatedSubstringPattern3("abcabc"));
	}
	//方法一：枚举
	public boolean repeatedSubstringPattern1(String s) {
        int n = s.length();
        //因为子串至少需要重复一次，所以i不会大于n的一半
        for (int i = 1; i * 2 <= n; ++i) {
        	//i为子字符串长度，如果不能被s的长度整除则不符合
            if (n % i == 0) {
                boolean match = true;
                //j为后指针，i为前指针，双指针移动过判断是否相同
                for (int j = i; j < n; ++j) {
                    if (s.charAt(j) != s.charAt(j - i)) {
                        match = false;
                        break;
                    }
                }
                if (match) {
                    return true;
                }
            }
        }
        return false;
    }
	//方法二：字符串匹配
	public boolean repeatedSubstringPattern2(String s) {
        return (s + s).indexOf(s, 1) != s.length();
    }
	//方法三：KMP 算法
	public boolean repeatedSubstringPattern3(String s) {
        return kmp(s + s, s);
    }
    public boolean kmp(String query, String pattern) {
        int n = query.length();
        int m = pattern.length();
        int[] fail = new int[m];
        Arrays.fill(fail, -1);
        //遍历pattern
        for (int i = 1; i < m; ++i) {
            int j = fail[i - 1];
            //pattern.charAt(i)按顺序比较，pattern.charAt(j + 1)即pattern.charAt(0);
            while (j != -1 && pattern.charAt(j + 1) != pattern.charAt(i)) {
            	//如果遇到不相等且j！=-1,那把j回退到fail[j]
                j = fail[j];
            }
            if (pattern.charAt(j + 1) == pattern.charAt(i)) {
            	//如果遇到相等fail[i]记录该字母坐标（j+1）
                fail[i] = j + 1;
            }
        }
        int match = -1;
        for (int i = 1; i < n - 1; ++i) {
        	//query.charAt(i)按顺序比较，pattern.charAt(match + 1)即pattern.charAt(0);
            while (match != -1 && pattern.charAt(match + 1) != query.charAt(i)) {
            	//如果遇到不相等且match！=-1,那把match回退到fail[match]
                match = fail[match];
            }
            if (pattern.charAt(match + 1) == query.charAt(i)) {
                ++match;
                //如果遇到相等，且match等于pattern的字符长度就返回true
                if (match == m - 1) {
                    return true;
                }
            }
        }
        return false;
    }
    //方法四：优化的 KMP 算法
    public boolean repeatedSubstringPattern4(String s) {
        return kmp2(s);
    }
    public boolean kmp2(String pattern) {
        int n = pattern.length();
        int[] fail = new int[n];
        Arrays.fill(fail, -1);
        for (int i = 1; i < n; ++i) {
            int j = fail[i - 1];
            while (j != -1 && pattern.charAt(j + 1) != pattern.charAt(i)) {
                j = fail[j];
            }
            if (pattern.charAt(j + 1) == pattern.charAt(i)) {
                fail[i] = j + 1;
            }
        }
        return fail[n - 1] != -1 && n % (n - fail[n - 1] - 1) == 0;
    }
}
